package com.didi.gulimall.common.aop;


import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @author 1062
 * @date 2020/3/16 14:29
 */
@Slf4j
@Aspect
@Component
public class InterfaceLogAspect {

    /**
     * 是否存在注解，如果存在就获取
     *
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(Log.class);
        }
        return null;
    }

    /**
     * 获取方法 注解描述
     *
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private ApiOperation getApiMethodDesc(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(ApiOperation.class);
        }
        return null;
    }

    /**
     * 环绕 切面
     *
     * @param joinPoint
     * @return
     */
    @SuppressWarnings("unused")
    @Around("execution(public * com.didi.gulimall.*.controller.*.*(..))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) {
        long requestTime = System.currentTimeMillis();
        Object result = null;
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        Signature signature = joinPoint.getSignature();
        // 方法名
        String methodName = signature.getName();
        log.info("methodName : " + methodName);
        // 记录下请求内容
        log.info("HTTP_METHOD : " + request.getMethod());
        log.info("uri : " + request.getRequestURI());
        //方法参数
        Object[] args = joinPoint.getArgs();
        if (args.length > 0) {
            Object[] arguments = new Object[args.length];
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
                    continue;
                }
                arguments[i] = args[i];
            }
            String params = JSONObject.toJSONString(arguments[0]);
            log.info("Request ==================== params : {},{}", request.getRequestURI(), params);
        }
        // 获得注解
        try {
            Log controllerLog = getAnnotationLog(joinPoint);
            ApiOperation apiOperation = getApiMethodDesc(joinPoint);
            if (apiOperation != null){
                log.info("方法描述 : " + apiOperation.value());
            }
            //执行目标方法
            result = joinPoint.proceed();
            //返回通知
            long cost = System.currentTimeMillis() - requestTime;
            log.info("Response ==================== : {},{}", request.getRequestURI(), JSONObject.toJSONString(result));
            log.info("Response ====================  : " + request.getRequestURI() + ": cost " + cost + "ms;");
            if (controllerLog == null || !controllerLog.isInsertLog()) {
                log.info("不插入日志信息");
                //执行目标方法
                return result;
            }
        } catch (Exception e) {
            log.info("Exception--------------------------捕获报错信息:{}", e.getMessage());
            e.printStackTrace();
        } catch (Throwable e) {
            log.info("Throwable--------------------------报错信息" + e);
            e.printStackTrace();
        }
        log.info("---->插入日志成功");
        return result;
    }
}
